﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using DsaPrimeGenerator.Annotations;
using DsaPrimeGenerator.Core;
using DsaPrimeGenerator.Mvvm;

namespace DsaPrimeGenerator
{
    public class MainViewModel : INotifyPropertyChanged
    {
        #region Fields

        #region Containers for bindable properties

        private BigInteger _seed;

        private BigInteger _q;

        private BigInteger _p;

        private int _l;

        private bool _isCalculating;

        private bool _showResult;

        private bool _generateSeedAutomatically;

        private string _message;

        #endregion

        #region Commands

        private ICommand _generateSeedCommand;

        private ICommand _generateLCommand;

        private ICommand _generatePrimesCommand;

        #endregion

        #region Services

        private readonly IBigIntegerRandom _bigIntegerRandom;

        private readonly IPrimeGenerator _primeGenerator;

        private readonly Random _random;

        #endregion

        #endregion

        #region Properties

        public string Message
        {
            get { return _message; }
            set
            {
                _message = value;
                OnPropertyChanged();
            }
        }

        public bool GenerateSeedAutomatically
        {
            get { return _generateSeedAutomatically; }
            set
            {
                _generateSeedAutomatically = value;
                OnPropertyChanged();
            }
        }

        public BigInteger Seed
        {
            get { return _seed; }
            set
            {
                _seed = value;
                OnPropertyChanged();
            }
        }

        public BigInteger Q
        {
            get { return _q; }
            set
            {
                _q = value;
                OnPropertyChanged();
            }
        }

        public BigInteger P
        {
            get { return _p; }
            set
            {
                _p = value;
                OnPropertyChanged();
            }
        }

        public int L
        {
            get { return _l; }
            set
            {
                _l = value;
                OnPropertyChanged();
            }
        }

        public bool IsCalculating
        {
            get { return _isCalculating; }
            set
            {
                _isCalculating = value;
                OnPropertyChanged();
            }
        }

        public bool ShowResult
        {
            get { return _showResult; }
            set
            {
                _showResult = value;
                OnPropertyChanged();
            }
        }

        public ICommand GenerateSeedCommand
        {
            get
            {
                return _generateSeedCommand ?? (_generateSeedCommand = new SimpleCommand(o => OnGenerateSeed(), o => true));
            }
        }

        public ICommand GenerateLCommand
        {
            get { return _generateLCommand ?? (_generateLCommand = new SimpleCommand(o => OnGenerateL(), o => true)); }
        }

        public ICommand GeneratePrimesCommand
        {
            get
            {
                return _generatePrimesCommand ??
                       (_generatePrimesCommand = new SimpleCommand(o => OnGeneratePrimes(), o => CanGeneratePrimes()));
            }
        }

        #endregion

        #region Constructors

        public MainViewModel(IPrimeGenerator primeGenerator, IBigIntegerRandom bigIntegerRandom)
        {
            if (ReferenceEquals(primeGenerator, null))
            {
                throw new ArgumentNullException("primeGenerator");
            }
            _primeGenerator = primeGenerator;

            if (ReferenceEquals(bigIntegerRandom, null))
            {
                throw new ArgumentNullException("bigIntegerRandom");
            }
            _bigIntegerRandom = bigIntegerRandom;

            _random = new Random();
            _showResult = false;
            _isCalculating = false;
            _message = "Введите зерно (или включите автоматический режим), размер степени L и начните расчет!";
        }

        #endregion

        #region Methods
        
        private void OnGenerateSeed()
        {
            Seed = _bigIntegerRandom.Next(20);
        }

        private void OnGenerateL()
        {
            int j = _random.Next(0, 8);
            L = 512 + 64 * j;
        }

        private async void OnGeneratePrimes()
        {
            //ShowResult = false;
            //IsCalculating = true;
            //GenerationResult result = await _primeGenerator.GenerateAsync(Seed, L);
            //if (result.IsSuccessful)
            //{
            //    Q = result.Q;
            //    P = result.P;
            //    ShowResult = true;
            //}
           
            //IsCalculating = false;

            Message = "Вычисляю...";
            IsCalculating = true;
            ShowResult = false;
            if (GenerateSeedAutomatically)
            {
                GenerationResult result;
                do
                {
                    Seed = _bigIntegerRandom.Next(20);
                    result = await _primeGenerator.GenerateAsync(Seed, L);
                } while (!result.IsSuccessful);
                Q = result.Q;
                P = result.P;
                ShowResult = true;
            }
            else
            {
                GenerationResult result = await _primeGenerator.GenerateAsync(Seed, L);
                if (result.IsSuccessful)
                {
                    Q = result.Q;
                    P = result.P;
                    ShowResult = true;
                }
                else
                {
                    Message = "Данное зерно не подходит. Попробуйте другое или используйте автоматический режим";
                }
            }
            IsCalculating = false;
        }

        private bool CanGeneratePrimes()
        {
            return L > 0 && (GenerateSeedAutomatically || Seed > 0);
        }

        #endregion

        #region Notify property changed implementation

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
           
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
    }
}
